<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>

        const obj = {
            msg: '哈喽',
            [Symbol('name')]: '小红',
        }

        const obj2 = {
            msg: '哈喽',
            name: '小红',
        }

        function Person(name, age) {
            this.name = name
            this.age = age
        }
        Person.prototype.sayHello = function () {
            console.log('hello')
        }
        
        console.log(obj)

        console.log(Object.keys(obj)) // ['msg']


        // 不可枚举属性举例：
        // 1.
        // Object.defineProperty(obj, 'name', {
        //     value: '小红',
        //     enumerable: false, // 不可枚举属性
        // })

        // 2.
        // obj[Symbol('name')] = '小红' // 不可枚举属性

        // 3.
        // Reflect.defineProperty(obj, 'name', {
        //     value: '小红',
        //     enumerable: false, // 不可枚举属性
        // })

        // 4.
        // Object.defineProperties(obj, {
        //     name: {
        //         value: '小红',
        //         enumerable: false, // 不可枚举属性
        //     },
        //     age: {
        //         value: 18,
        //         enumerable: true, // 可枚举属性
        //     }
        // })

        // Object.defineProperty和definePropertys用法:
        // 1. 定义一个对象，包含一个可枚举属性和一个不可枚举属性

        // 何为可枚举属性？
        // 可枚举属性是指可以被for...in循环、Object.keys()、Object.entries()等方法遍历到的属性



        console.log(Object.getOwnPropertyNames(obj)) // ['msg']
        // getOwnPropertyNames() 方法返回一个数组方法返回一个数组，包含指定对象自身的所有属性名（包括不可枚举属性，但不包括 Symbol 属性
        // 该方法返回的数组中不包含原型链上的属性名

        console.log(Object.getOwnPropertySymbols(obj)) // []
        // getOwnPropertySymbols() 方法返回一个数组，包含对象自身的所有 Symbol 属性键，不包括字符串类型的属性键

        console.log(Object.entries(obj)) // [['msg', '哈喽']]
        // entries() 方法返回一个数组，包含对象自身的所有可枚举属性键值对（包括字符串和 Symbol 属性键）
        // 该方法返回的数组中不包含原型链上的属性键值对
        // 该方法返回的数组中不包含不可枚举属性键值对
        // 该方法返回的数组中不包含 Symbol 属性键值对

        console.log('getOwnPropertyDescriptors', Object.getOwnPropertyDescriptors(obj)) // {msg: {…}}
        // getOwnPropertyDescriptors() 方法返回一个对象，包含对象自身的所有属性描述符（包括不可枚举属性和 Symbol 属性键）

        console.log(Object.getOwnPropertyDescriptor(obj, 'msg')) // {value: '哈喽', writable: true, enumerable: true, configurable: true}
        // getOwnPropertyDescriptor() 方法返回一个对象，包含指定属性的描述符（包括不可枚举属性和 Symbol 属性键）

        console.log(Object.getOwnPropertyDescriptor(obj, 'name')) // undefined

        console.log(Object.getPrototypeOf(obj))
        console.log(Object.getPrototypeOf(Person)) 
        // getPrototypeOf() 方法返回对象的原型（即对象的隐式原型）

        console.log(Object.is(obj, obj2)) // false
        // is() 方法用于判断两个值是否严格相等（包括 NaN 和 -0、+0 的比较）
        // 该方法与 === 运算符类似，但更严格
        // 该方法不会进行类型转换，只有当两个值的类型和内容都相同时，才返回 true

        console.log(Object.isExtensible(obj)) // true
        // isExtensible() 方法用于判断对象是否可扩展（即是否可以添加新属性）
        // 该方法返回一个布尔值，如果对象可扩展，则返回 true，否则返回 false

        const obj3 = {
            msg: '哈喽3',
        }
        Object.seal(obj3) // 密封对象，不能添加、删除或修改属性
        console.log(Object.isSealed(obj), Object.isSealed(obj3)) // false true
        // isSealed() 方法用于判断对象是否密封（即是否不能添加、删除或修改属性）
        // 该方法返回一个布尔值，如果对象密封，则返回 true，否则返回 false
        // 该方法不会判断对象的原型链上的属性是否密封
        // 如何使该对象密封？

        const obj4 = {
            msg: '哈喽4',
        }
        Object.freeze(obj4) // 冻结对象，不能添加、删除或修改属性
        console.log(Object.isFrozen(obj)) // false
        // isFrozen() 方法用于判断对象是否被冻结（即是否不能添加、删除或修改属性）
        // 该方法返回一个布尔值，如果对象冻结，则返回 true，否则返回 false
        // 如何使该对象冻结？
        // 1. 使用 Object.freeze() 方法冻结对象

        // 2. 使用 Object.defineProperty() 方法定义不可变属性
        const obj5 = {
            msg: '哈喽5',
        }
        Object.defineProperty(obj5, 'name', {
            value: '小红',
            writable: false, // 不可写属性
            enumerable: false, // 不可枚举属性
            configurable: false, // 不可配置属性
        })
        // 3. 使用 Object.defineProperties() 方法定义不可变属性 
        const obj6 = {
            msg: '哈喽6',
        }
        Object.defineProperties(obj6, {
            name: {
                value: '小红',
                writable: false, // 不可写属性
                enumerable: false, // 不可枚举属性
                configurable: false, // 不可配置属性
            },
            age: {
                value: 18,
                writable: false, // 不可写属性
                enumerable: false, // 不可枚举属性
                configurable: false, // 不可配置属性
            }
        })

        // Object.seal和Object.freeze的区别：
        // 1. Object.seal() 方法只会阻止对象添加和删除属性，但允许修改属性的值  
        // 2. Object.freeze() 方法会阻止对象添加、删除和修改属性的值


        console.log(obj.name) // undefined
        console.log(Person.name) // Person 
        // name() 方法用于获取对象的名称（即对象的构造函数的名称）

        // length() 方法用于获取对象的长度（即对象的属性个数）
        console.log(obj.length) // {msg: '哈喽'}
        console.log(Person.length) // 2

        console.log(obj instanceof Object) // true
        console.log(obj instanceof Person) // false

        console.log(Object.prototype.toString.call(obj)) // [object Object]
        console.log(Object.prototype.toString.call(obj2)) // [object Object]
        console.log(Object.prototype.toString.call(Person)) // [object Function]
        console.log(Object.prototype.toString.call([''])) // [object Array]
        console.log(Object.prototype.toString.call(new Person())) // [object Object]
        console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
        console.log(Object.prototype.toString.call(null)) // [object Null]
        console.log(Object.prototype.toString.call(true)) // [object Boolean]
        console.log(Object.prototype.toString.call(123)) // [object Number]
        console.log(Object.prototype.toString.call('123')) // [object String]
        console.log(Object.prototype.toString.call(Symbol('123'))) // [object Symbol]
        console.log(Object.prototype.toString.call(BigInt(123))) // [object BigInt]
        console.log(Object.prototype.toString.call(new Date())) // [object Date]
        console.log(Object.prototype.toString.call(/123/)) // [object RegExp]
        console.log(Object.prototype.toString.call(new Map())) // [object Map]
        console.log(Object.prototype.toString.call(new Set())) // [object Set]
        console.log(Object.prototype.toString.call(new WeakMap())) // [object WeakMap]
        console.log(Object.prototype.toString.call(new WeakSet())) // [object WeakSet]
        console.log(Object.prototype.toString.call(new Promise(() => {}))) // [object Promise]
        console.log(Object.prototype.toString.call(new ArrayBuffer(10))) // [object ArrayBuffer]
        console.log(Object.prototype.toString.call(new DataView(new ArrayBuffer(10)))) // [object DataView]
        console.log(Object.prototype.toString.call(new Int8Array(10))) // [object Int8Array]
        console.log(Object.prototype.toString.call(new Uint8Array(10))) // [object Uint8Array]
        console.log(Object.prototype.toString.call(new Uint8ClampedArray(10))) // [object Uint8ClampedArray]
        console.log(Object.prototype.toString.call(new Int16Array(10))) // [object Int16Array]
        console.log(Object.prototype.toString.call(new Uint16Array(10))) // [object Uint16Array]
        console.log(Object.prototype.toString.call(new Int32Array(10))) // [object Int32Array]
        console.log(Object.prototype.toString.call(new Uint32Array(10))) // [object Uint32Array]
        console.log(Object.prototype.toString.call(new Float32Array(10))) // [object Float32Array]
        console.log(Object.prototype.toString.call(new Float64Array(10))) // [object Float64Array]
        console.log(Object.prototype.toString.call(new BigInt64Array(10))) // [object BigInt64Array]
        console.log(Object.prototype.toString.call(new BigUint64Array(10))) // [object BigUint64Array]
        console.log(Object.prototype.toString.call(new ArrayBuffer(10))) // [object ArrayBuffer]
        console.log(Object.prototype.toString.call(new DataView(new ArrayBuffer(10)))) // [object DataView]
        // 返回结果[object Object] 数组中第一个元素是对象的类型，第二个元素是对象的构造函数的名称
        
        // Int8Array如何创建，使用，转换，判断
        // 1. 创建Int8Array对象
        const int8Array = new Int8Array(10) // 创建一个长度为10的Int8Array对象，默认值为0
        console.log(int8Array) // Int8Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        // 2. 创建Int8Array对象并赋值
        const int8Array2 = new Int8Array([1, 2, 3, 4, 5]) // 创建一个长度为5的Int8Array对象，值为[1, 2, 3, 4, 5]
        console.log(int8Array2) // Int8Array(5) [1, 2, 3, 4, 5]
        // 3. 转换为普通数组
        const arr = Array.from(int8Array2) // 转换为普通数组
        console.log(arr) // [1, 2, 3, 4, 5]
        // 4. 判断是否为Int8Array对象
        console.log(int8Array2 instanceof Int8Array) // true
        console.log(int8Array2 instanceof Array) // true
        // 5. 判断是否为普通数组
        console.log(Array.isArray(int8Array2)) // false
        console.log(Array.isArray(arr)) // true
        // 6. 判断是否为对象
        console.log(typeof int8Array2) // object
        console.log(typeof arr) // object
        // 7. 判断是否为函数
        console.log(typeof int8Array2) // object
        console.log(typeof Array) // function

        const obj7 = {
            msg: '哈喽7',
        }
        Object.preventExtensions(obj7) // 防止对象扩展，不能添加新属性
        console.log(Object.isExtensible(obj7)) // false
        obj.job = '前端' // 添加新属性失败
        console.log(obj7) // {msg: '哈喽7'}

        // 设置对象不可扩展属性
        // 1. 使用 Object.preventExtensions() 方法设置对象不可扩展属性

        // 2. 使用 Object.defineProperty() 方法设置对象不可扩展属性

        // 3. 使用 Object.defineProperties() 方法设置对象不可扩展属性
        // 4. 使用 Object.create() 方法设置对象不可扩展属性

        // Object.create({
        //     // 参数：
        //     // 1. prototype：要继承的对象
        //     // 2. propertiesObject：要添加的属性描述符对象（可选）
        // })

        const createObject = Object.create({
            name: '小红',
            age: 18,
        })
        console.log('createObject', createObject, JSON.stringify(createObject)) // {name: '小红', age: 18}

        // 5. 使用 Object.assign() 方法设置对象不可扩展属性
        const obj8 = {
            msg: '哈喽8',
        }
        Object.assign(obj8, {
            name: '小红',
            age: 18,
        }) // 合并对象，添加新属性
        console.log(obj8) // {msg: '哈喽8', name: '小红', age: 18}

        // 设置对象可扩展属性
        // 1. 使用 Object.isExtensible() 方法设置对象可扩展属性
        const obj9 = {
            msg: '哈喽9',
        }
        Object.isExtensible(obj9) // 设置对象可扩展属性
        console.log(Object.isExtensible(obj9)) // true
        obj9.job = '前端' // 添加新属性成功
        console.log(obj9) // {msg: '哈喽9', job: '前端'}

        


    </script>
</body>
</html>